﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Soft.Service
{
    public sealed class TimeSign : SoftObject
    {
        public static TimeSign Empty()
        {
            return new TimeSign
            {
                Precision = TimeUnit.NotSet
            };
        }

        public TimeSign()
        {
            this.mPrecision = TimeUnit.Day;
            this.mYear = 1;
            this.mMonth = 1;
            this.mDay = 1;
            this.mHour = 0;
            this.mMinute = 0;
            this.mSecond = 0;
            this.mDayOfWeek = 1;
        }


        public TimeUnit Precision
        {
            get
            {
                return this.mPrecision;
            }
            set
            {
                if (this.mPrecision != value)
                {
                    if (this.mPrecision >= TimeUnit.Month)
                    {
                        this.mPrecision = value;
                        return;
                    }
                    this.mPrecision = value;
                    if (value >= TimeUnit.Month)
                    {
                        this.Day = this.mDay;
                    }
                }
            }
        }


        public int Year
        {
            get
            {
                return this.mYear;
            }
            set
            {
                if (value >= 1 && value <= 9999)
                {
                    this.mYear = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Year", "范围从：1 到 9999");
            }
        }

        public int Month
        {
            get
            {
                return this.mMonth;
            }
            set
            {
                if (value >= 1 && value <= 12)
                {
                    this.mMonth = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Month", "范围从：1 到 12");
            }
        }

        public int DayOfWeek
        {
            get
            {
                return this.mDayOfWeek;
            }
            set
            {
                if (value >= 1 && value <= 7)
                {
                    this.mDayOfWeek = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("DayOfWeek", "范围从：1 到 7");
            }
        }

   
        public int Day
        {
            get
            {
                return this.mDay;
            }
            set
            {
                int num = 31;
                if (this.Precision >= TimeUnit.Month)
                {
                    if ((this.mMonth < 7 && this.mMonth % 2 == 0) || (this.mMonth > 8 && this.mMonth % 2 > 0))
                    {
                        num = 30;
                    }
                    if (this.mMonth == 2)
                    {
                        if (this.mYear.IsLeapYear())
                        {
                            num = 29;
                        }
                        else
                        {
                            num = 28;
                        }
                    }
                }
                if (value >= 1 && value <= num)
                {
                    this.mDay = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Day", "范围从：1 到 " + num.ToString());
            }
        }

        public int Hour
        {
            get
            {
                return this.mHour;
            }
            set
            {
                if (value >= 0 && value <= 59)
                {
                    this.mHour = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Hour", "范围从：0 到 59");
            }
        }


        public int Minute
        {
            get
            {
                return this.mMinute;
            }
            set
            {
                if (value >= 0 && value <= 59)
                {
                    this.mMinute = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Minute", "范围从：0 到 59");
            }
        }

        public int Second
        {
            get
            {
                return this.mSecond;
            }
            set
            {
                if (value >= 0 && value <= 59)
                {
                    this.mSecond = value;
                    return;
                }
                throw new ArgumentOutOfRangeException("Second", "范围从：0 到 59");
            }
        }


        /// <summary>
        /// 是否是执行的时间
        /// </summary>
        /// <param name="nowTime"></param>
        /// <returns></returns>
        public bool IsItsTime(DateTime nowTime)
        {

            //如果时间精度是按周计算
            if (this.mPrecision == TimeUnit.Week)
            {
                //如果当前时间是本周日
                if (nowTime.DayOfWeek == System.DayOfWeek.Sunday)
                {
                    if (this.mDayOfWeek != 7)
                    {
                        return false;
                    }
                }
                else if (this.mDayOfWeek != (int)nowTime.DayOfWeek)
                {
                    return false;
                }
                return this.mHour == nowTime.Hour && this.mMinute == nowTime.Minute && this.mSecond == nowTime.Second;
            }
            return (this.mPrecision < TimeUnit.Year || this.mYear == nowTime.Year) && (this.mPrecision < TimeUnit.Month || this.mMonth == nowTime.Month) && (this.mPrecision < TimeUnit.Day || this.mDay == nowTime.Day) && (this.mPrecision < TimeUnit.Hour || this.mHour == nowTime.Hour) && (this.mPrecision < TimeUnit.Minute || this.mMinute == nowTime.Minute) && (this.mPrecision < TimeUnit.Second || this.mSecond == nowTime.Second);
        }

        public override string ToString()
        {
            if (this.Precision == TimeUnit.Week)
            {
                return "W" + this.mDayOfWeek.ToString() + " " + this.DateTimeToText(TimeUnit.Hour);
            }
            return this.DateTimeToText(this.mPrecision);
        }

        public static TimeSign TryParse(string value, bool returnDefValue)
        {
            TimeSign result = null;
            try
            {
                result = TimeSign.Parse(value);
            }
            catch
            {
                if (returnDefValue)
                {
                    result = new TimeSign();
                }
            }
            return result;
        }


        public static TimeSign Parse(string value)
        {
            TimeSign timeSign = new TimeSign();
            if (string.IsNullOrWhiteSpace(value))
            {
                return TimeSign.Empty();
            }
            value = value.ToUpper();
            timeSign.Precision = TimeUnit.NotSet;
            if (ToolsClass.RegexTest("^W[1-7]\\s?", value))
            {
                Regex regex = new Regex("(?<=(W))\\d");
                timeSign.DayOfWeek = regex.Match(value).Value.ConvertTo(0, true);
                timeSign.Precision = TimeUnit.Week;
                value = value.Remove(0, 2);
            }
            if (ToolsClass.RegexTest("^(\\d{1,4}-)?(\\d{1,2}-)?\\d{0,2}\\s?(\\d{1,2}:)?(\\d{1,2}:)?\\d{1,2}$", value))
            {
                Regex regex2 = new Regex("\\d+");
                MatchCollection matchCollection = regex2.Matches(value);
                Match[] array = new Match[matchCollection.Count];
                matchCollection.CopyTo(array, 0);
                array = array.Reverse<Match>().ToArray<Match>();
                try
                {
                    if (matchCollection.Count <= 0)
                    {
                        throw new Exception("日期格式错误");
                    }
                    if (matchCollection.Count >= 1)
                    {
                        timeSign.Second = array[0].Value.ConvertTo(0, true);
                    }
                    if (matchCollection.Count >= 2)
                    {
                        timeSign.Minute = array[1].Value.ConvertTo(0, true);
                    }
                    if (matchCollection.Count >= 3)
                    {
                        timeSign.Hour = array[2].Value.ConvertTo(0, true);
                    }
                    if (matchCollection.Count >= 4)
                    {
                        timeSign.Day = array[3].Value.ConvertTo(0, true);
                    }
                    if (matchCollection.Count >= 5)
                    {
                        timeSign.Month = array[4].Value.ConvertTo(0, true);
                    }
                    if (matchCollection.Count >= 6)
                    {
                        timeSign.Year = array[5].Value.ConvertTo(0, true);
                    }
                    if (timeSign.Precision == TimeUnit.NotSet)
                    {
                        timeSign.Precision = matchCollection.Count.ConvertTo(TimeUnit.NotSet, true);
                        if (timeSign.Precision > TimeUnit.Day)
                        {
                            TimeSign timeSign2 = timeSign;
                            TimeUnit precision = timeSign2.Precision;
                            timeSign2.Precision = precision + 1;
                        }
                        if (timeSign.Precision >= TimeUnit.Quarter)
                        {
                            TimeSign timeSign3 = timeSign;
                            TimeUnit precision = timeSign3.Precision;
                            timeSign3.Precision = precision + 1;
                        }
                    }
                }
                catch
                {
                    throw new FormatException("日期格式错误");
                }
            }
            return timeSign;
        }


        private string DateTimeToText(TimeUnit precision)
        {
            string text = "";
            if (precision >= TimeUnit.Year)
            {
                text = text + this.mYear.ToString("0000") + "-";
            }
            if (precision >= TimeUnit.Month)
            {
                text = text + this.mMonth.ToString("00") + "-";
            }
            if (precision >= TimeUnit.Day)
            {
                text = text + this.mDay.ToString("00") + " ";
            }
            if (precision >= TimeUnit.Hour)
            {
                text = text + this.mHour.ToString("00") + ":";
            }
            if (precision >= TimeUnit.Minute)
            {
                text = text + this.mMinute.ToString("00") + ":";
            }
            if (precision >= TimeUnit.Second)
            {
                text += this.mSecond.ToString("00");
            }
            if (precision == TimeUnit.Second)
            {
                text += "s";
            }
            return text;
        }

        public static explicit operator string(TimeSign timeSign)
        {
            if (timeSign == null)
            {
                return null;
            }
            return timeSign.ToString();
        }


        public static explicit operator TimeSign(string value)
        {
            return TimeSign.Parse(value);
        }


        /// <summary>
        /// 时间精度
        /// </summary>
        private TimeUnit mPrecision;


        private int mYear;


        private int mMonth;


        private int mDayOfWeek;

        private int mDay;

        private int mHour;

        private int mMinute;

     
        private int mSecond;
    }
}
